package transformnumber

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/liujiawm/gocalendar"
)

type Transform struct {
	allNum         string
	allUnit        string
	cn2an          func(string, string) (float64, error)
	an2cn          func(string, string) (string, error)
	cnPattern      *regexp.Regexp
	smartCnPattern *regexp.Regexp
}

func NewTransform() *Transform {
	t := &Transform{
		allNum:  "零一二三四五六七八九",
		allUnit: strings.Join(getKeys(UNIT_CN2AN), ""),
		cn2an:   NewCn2An().Cn2An,
		an2cn:   NewAn2Cn().An2Cn,
	}
	t.cnPattern = regexp.MustCompile(fmt.Sprintf("负?([%s%s]+点)?[%s%s]+", t.allNum, t.allUnit, t.allNum, t.allUnit))
	t.smartCnPattern = regexp.MustCompile(fmt.Sprintf("-?([0-9]+.)?[0-9]+[%s]+", t.allUnit))
	return t
}

func (t *Transform) Transform(inputs string, method string) (string, error) {
	switch method {
	case "cn2an":
		inputs = strings.NewReplacer("廿", "二十", "卅", "三十", "半", "0.5", "俩", "2个", "仨", "3个", "〇", "零").Replace(inputs)

		// Date
		inputs = t.cnPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "cn2an", "date")
		})

		// Fraction
		fractionPattern := regexp.MustCompile(fmt.Sprintf("%s分之%s", t.cnPattern, t.cnPattern))
		inputs = fractionPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "cn2an", "fraction")
		})

		// Percent
		percentPattern := regexp.MustCompile(fmt.Sprintf("百分之%s", t.cnPattern))
		inputs = percentPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "cn2an", "percent")
		})

		// Celsius
		celsiusPattern := regexp.MustCompile(fmt.Sprintf("%s摄氏度", t.cnPattern))
		inputs = celsiusPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "cn2an", "celsius")
		})

		// Number
		output := t.cnPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "cn2an", "number")
		})

		return output, nil

	case "an2cn":
		// date
		datePattern := regexp.MustCompile(`(\d{1,4}年)?(\d{1,2}月)?(\d{1,2}日)?`)
		inputs = datePattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "an2cn", "date")
		})

		// fraction
		fractionPattern := regexp.MustCompile(`\d+/\d+`)
		inputs = fractionPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "an2cn", "fraction")
		})

		// percent
		percentPattern := regexp.MustCompile(`-?(\d+\.)?\d+%`)
		inputs = percentPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "an2cn", "percent")
		})

		// celsius
		celsiusPattern := regexp.MustCompile(`\d+℃`)
		inputs = celsiusPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "an2cn", "celsius")
		})

		// number
		numberPattern := regexp.MustCompile(`-?(\d+\.)?\d+`)
		output := numberPattern.ReplaceAllStringFunc(inputs, func(s string) string {
			return t.subUtil(s, "an2cn", "number")
		})

		return output, nil

	default:
		return "", fmt.Errorf("error method: %s, only support 'cn2an' and 'an2cn'", method)
	}
}

func (t *Transform) subUtil(inputs string, method string, subMode string) string {
	if inputs == "" {
		return inputs
	}
	runeInputs := []rune(inputs)
	lenRuneInputs := len(runeInputs)

	var result string
	var err error

	switch method {
	case "cn2an":
		switch subMode {
		case "date":
			result = t.smartCnPattern.ReplaceAllStringFunc(inputs, func(s string) string {
				num, _ := t.cn2an(s, "smart")
				return fmt.Sprintf("%v", num)
			})
			result = t.cnPattern.ReplaceAllStringFunc(result, func(s string) string {
				num, _ := t.cn2an(s, "smart")
				return fmt.Sprintf("%v", num)
			})
		case "fraction":
			if !strings.HasPrefix(inputs, "百") {
				fracResult := t.cnPattern.ReplaceAllStringFunc(inputs, func(s string) string {
					num, _ := t.cn2an(s, "smart")
					return fmt.Sprintf("%v", num)
				})
				parts := strings.Split(fracResult, "分之")
				if len(parts) == 2 {
					result = fmt.Sprintf("%s/%s", parts[1], parts[0])
				} else {
					result = inputs
				}
			} else {
				result = inputs
			}
		case "percent":
			result = regexp.MustCompile(`(?<=百分之)`+t.cnPattern.String()).ReplaceAllStringFunc(inputs, func(s string) string {
				num, _ := t.cn2an(s, "smart")
				return fmt.Sprintf("%v", num)
			})
			result = strings.ReplaceAll(result, "百分之", "") + "%"
		case "celsius":
			result = regexp.MustCompile(t.cnPattern.String()+`(?=摄氏度)`).ReplaceAllStringFunc(inputs, func(s string) string {
				num, _ := t.cn2an(s, "smart")
				return fmt.Sprintf("%v", num)
			})
			result = strings.ReplaceAll(result, "摄氏度", "℃")
		case "number":
			num, _ := t.cn2an(inputs, "smart")
			result = fmt.Sprintf("%v", num)
		default:
			err = fmt.Errorf("error sub_mode: %s", subMode)
		}
	case "an2cn":
		switch subMode {
		case "date":
			result = regexp.MustCompile(`\d+(?=年)`).ReplaceAllStringFunc(inputs, func(s string) string {
				cn, _ := t.an2cn(s, "direct")
				return cn
			})
			result = regexp.MustCompile(`\d+`).ReplaceAllStringFunc(result, func(s string) string {
				cn, _ := t.an2cn(s, "low")
				return cn
			})
		case "fraction":
			fracResult := regexp.MustCompile(`\d+`).ReplaceAllStringFunc(inputs, func(s string) string {
				cn, _ := t.an2cn(s, "low")
				return cn
			})
			parts := strings.Split(fracResult, "/")
			if len(parts) == 2 {
				result = fmt.Sprintf("%s分之%s", parts[1], parts[0])
			} else {
				result = inputs
			}
		case "celsius":
			cn, _ := t.an2cn(inputs[:lenRuneInputs-1], "low")
			result = cn + "摄氏度"
		case "percent":
			cn, _ := t.an2cn(inputs[:lenRuneInputs-1], "low")
			result = "百分之" + cn
		case "number":
			result, _ = t.an2cn(inputs, "low")
		default:
			err = fmt.Errorf("error sub_mode: %s", subMode)
		}
	default:
		err = fmt.Errorf("error method: %s,", method)
	}

	if err != nil {
		fmt.Printf("Warning: %v, origValue: %s\n", err, inputs)
		return inputs
	}

	return result
}

// 时间转换结果, 有可能是一个时间段, 也可能只有一个时间
type RespDatetime struct {
	StartTime DatetimeItem
	EndTime   DatetimeItem
}

type DatetimeItem struct {
	Datetime            string // 字符串的时间(标准时间: 2024-02-29 12:00:00 +08:00)
	DatetimeOriginal    string // 原始字符串表示的时间
	Year                string // 年
	Month               string // 月
	Day                 string // 日
	Hour                string // 时
	Minute              string // 分
	Second              string // 秒
	Timezone            string // 时区
	ChineseCalenderFlag bool   // 农历标志(标记原始时间是否为农历)
}

func (t *Transform) TfDatetime(inputs string) (string, error) {

	inputs = strings.NewReplacer("廿", "二十", "卅", "三十", "俩", "2个", "仨", "3个", "〇", "零", "点", "时").Replace(inputs)

	// 替换大写为小写
	inputs = strings.NewReplacer("壹", "一", "贰", "二", "叁", "三", "肆", "四", "伍", "五", "陆", "六", "柒", "七", "捌", "八", "玖", "九", "拾", "十").Replace(inputs)

	// 先处理具体的时间
	inputs, addDayFlag, err := t.TransformChineseTimeofHour(inputs)
	if err != nil {
		return "", err
	}

	// 处理年份
	inputs, err = t.TransformChineseTimeofYear(inputs)
	if err != nil {
		return "", err
	}

	// 替换月份和日期
	inputs, chineseCalFlag, leapFlag, jumpDay, err := t.TransformChineseTimeofMonth(inputs)
	if err != nil {
		return "", err
	}

	// 转换为纯数字
	dateReg := regexp.MustCompile(`((\d{1,4})\s*年)?\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日`)
	inputs = dateReg.ReplaceAllStringFunc(inputs, func(s string) string {
		s = removeSpace(s)
		if strings.Contains(s, "年") {
			s = strings.Replace(s, "年", "-", 1)
		} else {
			// 当没有年份的时候, 需要根据当前年份进行补充
			s = fmt.Sprintf("%d-%s", time.Now().Year(), s)
		}
		if strings.Contains(s, "月") {
			s = strings.Replace(s, "月", "-", 1)

		}
		if strings.Contains(s, "日") {
			s = strings.Replace(s, "日", "", 1)
		}
		return s
	})

	inputs, _ = t.TransChineseToGregorian(inputs, chineseCalFlag, leapFlag, addDayFlag, jumpDay)

	return inputs, nil

}

func (t *Transform) TransChineseToGregorian(inputs string, chineseCalFlag bool, leapFlag bool, addDayFlag int, jumpDay int) (string, error) {
	var year, month, day int

	dateReg := regexp.MustCompile(`\d{1,4}-\d{1,2}-\d{1,2} `)
	inputs = dateReg.ReplaceAllStringFunc(inputs, func(s string) string {
		s = removeSpace(s)
		dateSlice := strings.Split(s, "-")
		year, _ = strconv.Atoi(dateSlice[0])
		month, _ = strconv.Atoi(dateSlice[1])
		day, _ = strconv.Atoi(dateSlice[2])

		gd, err := time.Parse("2006-1-2", s)
		if err != nil {
			fmt.Printf("parse.format(2006-1-2), Error: %v, originVale: %s\n", err, inputs)

			gd, err = time.Parse("2006-01-02", s)
			if err != nil {
				fmt.Printf("parse.format(2006-01-02), Error: %v, originVale: %s\n", err, inputs)
				return ""
			}
		}
		if chineseCalFlag {
			// 对农历进行转换处理
			c := gocalendar.DefaultCalendar()

			gd1, err := c.LunarToGregorian(year, month, day, leapFlag)
			if err != nil {
				fmt.Printf("Error: %v, origValue: %s\n", err, inputs)
				return inputs + " (农历转换错误:" + err.Error() + ")"
			}
			gd = gd1
		}

		if addDayFlag != 0 {
			gd = gd.Add(time.Duration(addDayFlag) * 24 * time.Hour)
		}

		if jumpDay != 0 {
			gd = gd.Add(time.Duration(jumpDay) * 24 * time.Hour)
		}

		return gd.Format("2006-01-02 ")
	})

	return inputs, nil
}
